package top.jolyoulu.codegenweb.observer;

import freemarker.template.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import top.jolyoulu.codegenweb.config.TemplateConfig;
import top.jolyoulu.codegenweb.entity.bo.form.MysqlGenCodeFormBO;
import top.jolyoulu.codegenweb.entity.po.*;
import top.jolyoulu.codegenweb.enums.MysqlModel;
import top.jolyoulu.codegenweb.service.bll.ApiMysqlDataSourceService;
import top.jolyoulu.codegenweb.service.dal.*;
import top.jolyoulu.corecommon.queue.delayed.DelayedTaskEntity;
import top.jolyoulu.corecommon.queue.delayed.observer.AbstractObserver;
import top.jolyoulu.corecommon.utils.io.FileUtils;
import top.jolyoulu.modules.druidmodule.entity.TableColumnInfo;
import top.jolyoulu.modules.druidmodule.utils.datasource.DruidUtils;
import top.jolyoulu.modules.druidmodule.utils.db.mysql.MysqlDBUtils;
import top.jolyoulu.webcommon.enums.GlobalExpType;
import top.jolyoulu.webcommon.excption.GlobalException;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.SQLException;
import java.util.*;

/**
 * @Author: JolyouLu
 * @Date: 2023/4/15 19:30
 * @Description
 */
@Slf4j
@Component
public class MysqlGenCodeObserver extends AbstractObserver<MysqlGenCodeFormBO> {

    @Autowired
    private DataSourceInfoService dataSourceInfoService;

    @Autowired
    private GenTemplateFileService genTemplateFileService;

    @Autowired
    private GenCodeTaskService genCodeTaskService;

    @Autowired
    private TemplateConfig templateConfig;

    @Autowired
    private FileDataService fileDataService;

    @Autowired
    private MysqlTableColumnMapService mysqlTableColumnMapService;

    @Autowired
    private GlobalVariableMapService globalVariableMapService;

    @Autowired
    private ApiMysqlDataSourceService apiMysqlDataSourceService;

    @Override
    protected void task(DelayedTaskEntity<MysqlGenCodeFormBO> task) {
        MysqlGenCodeFormBO bo = task.getTask();
        GenCodeTask codeTask = genCodeTaskService.getById(task.getTaskId());
        //非空，并且在生成中时才执行
        if (Objects.nonNull(codeTask) && codeTask.getProgress().equals(1)) {
            MysqlDBUtils dbUtils = null;
            try {
                String directory = templateConfig.getDirectory();
                //初始化任务生成代码的路径
                String basePath = directory + File.separator + "code" + File.separator + task.getTaskId();
                DataSourceInfo dataSourceInfo = dataSourceInfoService.getById(bo.getDataSourceId());
                if (Objects.isNull(dataSourceInfo)) {
                    throw new GlobalException(GlobalExpType.PARAM_ERROR);
                }
                //循环获取表信息
                apiMysqlDataSourceService.testConnectionById(dataSourceInfo.getId());
                DataSource dataSource = DruidUtils.getDataSource(dataSourceInfo.getId());
                dbUtils = MysqlDBUtils.build(dataSource.getConnection());
                //设置模型数据
                HashMap<String, Object> map = new HashMap<>();
                globalModel(map);
                for (String tableName : bo.getTableNames()) {
                    tableModel(map,tableName);
                    List<TableColumnInfo> tableColumns = dbUtils.selectTableColumns(dataSourceInfo.getSchemaName(), tableName);
                    tableColumnModel(map,tableColumns);
                    //获取模板文件
                    for (String templateFileId : bo.getTemplateFileIds()) {
                        GenTemplateFile tempFile = genTemplateFileService.getById(templateFileId);
                        if (Objects.nonNull(tempFile)) {
                            //文件生成的路径
                            String javaFilePathBasic = basePath + File.separator + (StringUtils.isBlank(tempFile.getModel()) ? tempFile.getTemplateName() : tempFile.getModel()) + File.separator;
                            File javaFilePath = FileUtils.get(javaFilePathBasic, mapString(map,tempFile.getGenFileName()));
                            //生成代码
                            FileData fileData = fileDataService.getById(tempFile.getFileId());
                            if (fileData.getFileExtension().equals("ftl")) {
                                //根据模板构建内容
                                File tempFilePath = createTemplateFile(tempFile.getFileId());
                                Configuration config = new Configuration();
                                //模板文件路径
                                config.setDirectoryForTemplateLoading(tempFilePath.getParentFile());
                                //设置模板的编码格式
                                config.setDefaultEncoding("utf-8");
                                //加载模板文件
                                Template template = config.getTemplate(tempFilePath.getName());
                                try (FileWriter writer = new FileWriter(javaFilePath)) {
                                    template.process(map, writer);
                                }
                            }
                        }
                    }
                }
                codeTask.setFilePath(basePath);
                codeTask.setProgress(2);
                genCodeTaskService.updateById(codeTask);
            } catch (SQLException | TemplateException | IOException e) {
                log.error("", e);
                codeTask.setErrorInfo(e.getMessage());
                codeTask.setProgress(3);
                genCodeTaskService.updateById(codeTask);
            } finally {
                if (Objects.nonNull(dbUtils)) {
                    dbUtils.close();
                }
            }
        }
    }

    /**
     * 创建模板文件
     *
     * @param fileId
     * @return
     */
    public File createTemplateFile(String fileId) {
        FileData fileData = fileDataService.getById(fileId);
        File file = FileUtils.get(templateConfig.getDirectory() + File.separator + "ftl", fileData.getFileName() + "." + fileData.getFileExtension());
        try (FileOutputStream fos = new FileOutputStream(file)) {
            fos.write(fileData.getFileByte());
            fos.flush();
        } catch (IOException e) {
            log.error("", e);
        }
        return file;
    }

    /**
     * 驼峰命名，首字母大写
     *
     * @param str
     * @return
     */
    public String toHumpHF(String str) {
        StringBuilder res = new StringBuilder();
        String[] split = str.split("_");
        for (String s : split) {
            if (s.length() > 0) {
                String upperCase = String.valueOf(s.charAt(0)).toUpperCase();
                res.append(upperCase).append(s.substring(1));
            }
        }
        return res.toString();
    }

    /**
     * 驼峰命名，首字母小写
     *
     * @param str
     * @return
     */
    public String toHumpLF(String str) {
        StringBuilder res = new StringBuilder();
        String[] split = str.split("_");
        for (int i = 0; i < split.length; i++) {
            String s = split[i];
            //首字母小写
            if (i == 0) {
                String upperCase = String.valueOf(s.charAt(0)).toLowerCase();
                res.append(upperCase).append(s.substring(1));
            } else {
                String upperCase = String.valueOf(s.charAt(0)).toUpperCase();
                res.append(upperCase).append(s.substring(1));
            }
        }
        return res.toString();
    }

    /***
     * 设置表模型数据
     * @param map
     */
    public void tableModel(HashMap<String, Object> map, String tableName) {
        map.put(MysqlModel.TABLE_NAME.getProperty(), tableName);
        map.put(MysqlModel.TABLE_TO_JAVA_BEAN.getProperty(), toHumpHF(tableName));
        map.put(MysqlModel.TABLE_TO_JAVA_BEAN_LF.getProperty(), toHumpLF(tableName));
    }

    /**
     * 设置表自动
     */
    public void tableColumnModel(HashMap<String, Object> map, List<TableColumnInfo> tableColumns) {
        List<Map<String, Object>> list = new ArrayList<>();
        for (TableColumnInfo tableColumn : tableColumns) {
            HashMap<String, Object> entity = new HashMap<>();
            //获取java映射类型
            MysqlTableColumnMap dataType = mysqlTableColumnMapService.getByDataType(1, tableColumn.getDataType());
            entity.put(MysqlModel.COLUMN_TO_JAVA_TYPE.getProperty(), Objects.isNull(dataType) ? "Object" : dataType.getMapType());
            entity.put(MysqlModel.COLUMN_TO_JAVA_FILED.getProperty(), toHumpHF(tableColumn.getColumnName()));
            entity.put(MysqlModel.COLUMN_TO_JAVA_FILED_LF.getProperty(), toHumpLF(tableColumn.getColumnName()));
            entity.put(MysqlModel.COLUMN_IS_PK.getProperty(), tableColumn.getColumnKey().equals("PRI"));
            entity.put(MysqlModel.COLUMN_COMMENT.getProperty(), tableColumn.getColumnComment());
            list.add(entity);
        }
        map.put(MysqlModel.TABLE_COLUMNS.getProperty(), list);
    }

    /**
     * 设置全局变量
     */
    public void globalModel(HashMap<String, Object> map) {
        List<GlobalVariableMap> list = globalVariableMapService.list();
        list.forEach(item -> {
            if (!map.containsKey(item.getProperty())) {
                map.put(item.getProperty(), item.getVariable());
            }
        });
    }

    /**
     * 解析String的插值${}
     * 从map中找到对应的映射
     */
    public String mapString(HashMap<String, Object> map,String str) {
        String substring = str.substring(str.indexOf("${") + 2, str.indexOf("}"));
        if (map.containsKey(substring)) {
            str = str.replaceAll("\\$\\{"+substring+"}",map.get(substring).toString());
        }
        return str;
    }
}
